'''
    Code Name: Python Pong 3D
    
    Module: PyPong
    Filename:  __init__.py
    
    Developed by: Juan C. Montes
    
    Created on 25/03/2009
'''


# Referencias de OGRE
import ogre.renderer.OGRE as ogre
import ogre.io.OIS as ois


# Referencias del proyecto
import Ball
import Paddle


''' Clase principal del juego '''
class PyPong(ogre.FrameListener, ois.KeyListener):    
    
    '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
    Function: Constructor
    '''
    def __init__(self):
        # Puntuaciones
        self.playerScore = 0
        self.cpuScore = 0
        
        # N de jugadores
        self.players = 1     
        
        # Definimos las constantes
        self.PADDLE_DOWN = ogre.Vector3(0, -0.1, 0)
        self.PADDLE_UP = ogre.Vector3(0, 0.1, 0)
        self.SPEED_INCREMENT = 10
        self.SPEED_DECREMENT = -10        
        
        # Estado del juego
        self.quit = False
        self.pause = False
        self.wait = True 
        self.time = 0   


    '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
    Function: run
    Descript: Funcion principal de carga del engine
    '''
    def run(self):
        self.createRoot()
        self.setupRenderSystem()
        self.createRenderWindow()        
        self.setupScene()
        self.setupInputSystem()
        self.createFrameListener()
        self.initialiseResourceGroups()        
        self.createScene()
        self.startRenderLoop()


    '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
    Function: createRoot
    Descript: Creamos la raiz del engine
    '''    
    def createRoot(self):
        self.root = ogre.Root()


    '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
    Function: setupRenderSystem
    Descript: Cargamos la config o usamos la que ya tenemos
    '''    
    def setupRenderSystem(self):
        if not self.root.restoreConfig() and not self.root.showConfigDialog():
            raise Exception("User canceled the config dialog!" 
                            "-> Application.setupRenderSystem()")


    '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
    Function: createRenderWindow
    Descript: Creamos la ventana principal
    '''  
    def createRenderWindow(self):
        self.window = self.root.initialise(True, "Python Pong 3D")


    '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
    Function: initialiseResourceGroups
    Descript: Cargamos los recursos del juego
    '''  
    def initialiseResourceGroups(self):        
        ogre.ResourceGroupManager.getSingleton().addResourceLocation(
                "./media/", "FileSystem", "PythonPong3D")
        ogre.ResourceGroupManager.getSingleton().initialiseAllResourceGroups()


    '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
    Function: setupScene
    Descript: Preparamos la escena cargando camaras, luces, viewports...
    '''  
    def setupScene(self):
        # Creamos el SceneManager
        self.scenemanager = ogre.SceneManager
        self.scenemanager = self.root.createSceneManager(ogre.ST_GENERIC)
        
        # Creamos y posicionamos la camara principal
        self.camera = ogre.Camera
        self.camera = self.scenemanager.createCamera("MainCamera")
        self.camera.setPosition(0, 0, 240)
        self.camera.lookAt(0, 0, 0)
        self.camera.setNearClipDistance(5)
        self.camera.setFarClipDistance(1000)
        
        # Creamos el punto de vista basado en la camara
        self.pViewport = ogre.Viewport
        self.pViewport = self.window.addViewport(self.camera)
        self.pViewport.setBackgroundColour(ogre.ColourValue(0, 0, 0))
        self.camera.setAspectRatio(self.pViewport.getActualWidth() / 
                                   self.pViewport.getActualHeight())
        
        # Ajustamos la luz general de la escena
        self.scenemanager.setAmbientLight(ogre.ColourValue(0.75, 0.75, 0.75))
        
        # Creamos un punto de luz
        self.light = ogre.Light
        self.light = self.scenemanager.createLight("Light")
        self.light.setPosition(0, 0, 50)


    '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
    Function: setupInputSystem
    Descript: Preparamos los controles del teclado
    ''' 
    def setupInputSystem(self):
        windowHandle = 0
        renderWindow = self.root.getAutoCreatedWindow()
        windowHandle = renderWindow.getCustomAttributeInt("WINDOW")
        paramList = [("WINDOW", str(windowHandle))]
        self.inputManager = ois.createPythonInputSystem(paramList)
        try:
            self.keyboard = self.inputManager.createInputObjectKeyboard(
                            ois.OISKeyboard, True)
        except Exception, e:
            raise e


    '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
    Function: createFrameListener
    Descript: Iniciamos el listener principal y del teclado
    ''' 
    def createFrameListener(self):       
        ois.KeyListener.__init__(self) 
        ogre.FrameListener.__init__(self)
        self.root.addFrameListener(self)
        

    '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
    Function: startRenderLoop
    Descript: Iniciamos el engine
    ''' 
    def startRenderLoop(self):            
        self.root.startRendering()


    '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
    Function: cleanUp
    Descript: Destruimos los objetos que hagan falta
    ''' 
    def cleanUp(self):
        try:
            self.inputManager
            self.inputManager.destroyInputObjectKeyboard(self.keyboard)
            ois.InputManager.destroyInputSystem(self.inputManager)
            self.inputManager = None
        except Exception, e:
            raise e
        
                
    '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
    Destructor
    ''' 
    def __del__(self):
        self.cleanUp()
        pass
    

    '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
    Function: updateScore
    Descript: Funcion para actualizar la puntuacion
    '''     
    def updateScore(self):
        # Mostramos la puntuacion del jugador        
        score = ogre.OverlayManager.getSingleton().getOverlayElement("left")
        score.setCaption("Player Score: %d" % self.playerScore)
        
        # Mostramos la puntuacion de la CPU
        score = ogre.OverlayManager.getSingleton().getOverlayElement("right")
        score.setCaption("CPU Score: %d" % self.cpuScore)
        
        # Paramos el juego en espera de restaurar la bola
        self.wait = True
        
        
    '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
    Function: createScene
    Descript: Crea la escene inicial
    ''' 
    def createScene(self):
        # Creamos el texto superior
        scoreOverlay = ogre.OverlayManager.getSingleton().getByName("Score")
        scoreOverlay.show()
        
        # Creamos los objetos del juego        
        self.ball = Ball.Ball(self.scenemanager, self)
        self.ball.addToScene()
        self.leftpaddle = Paddle.Paddle(self.scenemanager, "LeftPaddle", -90)
        self.leftpaddle.addToScene()
        self.rightpaddle = Paddle.Paddle(self.scenemanager, "RightPaddle", 90)
        self.rightpaddle.addToScene()
        
        # Creamos el muro izq
        ent = ogre.Entity
        ent = self.scenemanager.createEntity("LWall", "cube.mesh")
        ent.setMaterialName("Wall")        
        node = ogre.SceneNode
        node = self.scenemanager.getRootSceneNode().createChildSceneNode(
               "LWall")
        node.attachObject(ent)
        node.setPosition(-95, 0, 0)
        node.setScale(0.05, 1.45, 0.1)
        
        # Creamos el muro der     
        ent = self.scenemanager.createEntity("RWall", "cube.mesh")
        ent.setMaterialName("Wall")        
        node = self.scenemanager.getRootSceneNode().createChildSceneNode(
               "RWall")
        node.attachObject(ent)
        node.setPosition(95, 0, 0)
        node.setScale(0.05, 1.45, 0.1)
        
        # Creamos el muro inferior
        ent = self.scenemanager.createEntity("BWall", "cube.mesh")
        ent.setMaterialName("Wall")        
        node = self.scenemanager.getRootSceneNode().createChildSceneNode(
               "BWall")
        node.attachObject(ent)
        node.setPosition(0, -70, 0)
        node.setScale(1.90, 0.05, 0.1)

        # Creamos el muro superior
        ent = self.scenemanager.createEntity("TWall", "cube.mesh")
        ent.setMaterialName("Wall")        
        node = self.scenemanager.getRootSceneNode().createChildSceneNode(
               "TWall")
        node.attachObject(ent)
        node.setPosition(0, 70, 0)
        node.setScale(1.90, 0.05, 0.1)


    '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
    Function: frameStarted
    Descript: Control del inicio del render
    ''' 
    def frameStarted(self, e):
        # Capturamos el teclado
        self.keyboard.capture()
        
        # Control del buffer de teclas
        if self.keyboard.buffered():
            self.processKeyBuffered()
                
        # Si el juego no esta parado y la bola debe moverse
        if not self.wait and not self.pause:
            # Movemos la bola y comprobamos colisiones
            self.ball.moveBall(e.timeSinceLastFrame, self.rightpaddle)
            self.ball.hitPaddle()
            
            # Con A y Z nos movemos
            
        elif self.wait:
            # Incrementamos el tiempo si es menos de 4s
            if self.time < 4:
                self.time += e.timeSinceLastFrame
            else:
                self.wait = False
                self.time = 0
                
        return not self.quit
    

    '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
    Function: processKeyBuffered
    Descript: Control de la pulsacion de las teclas
    ''' 
    def processKeyBuffered(self):
        # Controlamos que el juego no este parado
        if not self.pause:
            # Controlamos la pulsacion de las teclas
            if self.keyboard.isKeyDown(ois.KC_ESCAPE):
                self.quit = True
                self.root.shutdown()
            elif self.keyboard.isKeyDown(ois.KC_A):
                self.leftpaddle.movePaddle(self.PADDLE_UP)
            elif self.keyboard.isKeyDown(ois.KC_Z):
                self.leftpaddle.movePaddle(self.PADDLE_DOWN)                
            elif self.keyboard.isKeyDown(ois.KC_F):
                self.ball.changeSpeed(self.SPEED_INCREMENT)
            elif self.keyboard.isKeyDown(ois.KC_S):
                self.ball.changeSpeed(self.SPEED_DECREMENT)
                
                
            
            
            
            
            
            
            
            
            
            
             
